<script setup>
import { ref, reactive, onMounted, inject } from "vue"
import CryptoJS from "crypto-js"
import copyw from "@/components/copy.vue"
import useClipboard from "vue-clipboard3"
import { message } from "ant-design-vue"

const globalVars = inject("globalVars")
const { toClipboard } = useClipboard()

const state = reactive({
    sourceValue: "",
    encryptData: "",
    decryptData: "",
    key: "",
    iv: "",
    mode: "CBC", // ECB 或 CBC
    keySize: "128", // 128, 192, 256
})

const copy = async (msg) => {
    try {
        await toClipboard(msg)
        message.success("复制成功")
    } catch (e) {
        message.error("复制失败")
    }
}

function encrypt() {
    if (!state.sourceValue || !state.key) {
        message.error("请输入待加密内容和密钥")
        return
    }

    try {
        // 根据密钥长度补全或截断密钥
        const keyBytes = parseInt(state.keySize) / 8
        const paddedKey = state.key.padEnd(keyBytes, "\0").slice(0, keyBytes)
        const keyWordArray = CryptoJS.enc.Utf8.parse(paddedKey)

        let encryptConfig = {
            mode: state.mode === "ECB" ? CryptoJS.mode.ECB : CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7,
            keySize: parseInt(state.keySize) / 32,
        }

        // CBC模式需要IV
        if (state.mode === "CBC") {
            if (!state.iv) {
                message.error("CBC模式需要输入IV")
                return
            }
            encryptConfig.iv = CryptoJS.enc.Utf8.parse(state.iv)
        }

        const encrypted = CryptoJS.AES.encrypt(state.sourceValue, keyWordArray, encryptConfig)
        state.encryptData = encrypted.toString()
    } catch (error) {
        console.error(error)
        message.error("加密失败：" + error.message)
        state.encryptData = "加密失败，请检查输入数据"
    }
}

function decrypt() {
    if (!state.sourceValue || !state.key) {
        message.error("请输入待解密内容和密钥")
        return
    }

    try {
        // 根据密钥长度补全或截断密钥
        const keyBytes = parseInt(state.keySize) / 8
        const paddedKey = state.key.padEnd(keyBytes, "\0").slice(0, keyBytes)
        const keyWordArray = CryptoJS.enc.Utf8.parse(paddedKey)

        let decryptConfig = {
            mode: state.mode === "ECB" ? CryptoJS.mode.ECB : CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7,
            keySize: parseInt(state.keySize) / 32,
        }

        // CBC模式需要IV
        if (state.mode === "CBC") {
            if (!state.iv) {
                message.error("CBC模式需要输入IV")
                return
            }
            decryptConfig.iv = CryptoJS.enc.Utf8.parse(state.iv)
        }

        const decrypted = CryptoJS.AES.decrypt(state.sourceValue, keyWordArray, decryptConfig)
        state.decryptData = decrypted.toString(CryptoJS.enc.Utf8)
        if (state.decryptData === "") {
            state.decryptData = "解密失败，请检查输入数据"
        }
    } catch (error) {
        console.error(error)
        // message.error("解密失败：" + error.message)
        state.decryptData = "解密失败，请检查输入数据"
    }
}

function change(save = true) {
    if (save) {
        saveToLocalStorage()
    }

    if (state.sourceValue === "") {
        state.encryptData = ""
        state.decryptData = ""
        return
    }
    encrypt()
    decrypt()
}
// 监听参数变化并保存到本地存储
function saveToLocalStorage() {
    const storageKey = globalVars.activeKey
    localStorage.setItem(storageKey, state.sourceValue)
    localStorage.setItem(`${storageKey}_key`, state.key)
    localStorage.setItem(`${storageKey}_iv`, state.iv)
    localStorage.setItem(`${storageKey}_mode`, state.mode)
    localStorage.setItem(`${storageKey}_keySize`, state.keySize)
}

onMounted(() => {
    // 从本地存储恢复所有参数
    const storageKey = globalVars.activeKey
    const sourceValue = localStorage.getItem(storageKey)
    const key = localStorage.getItem(`${storageKey}_key`)
    const iv = localStorage.getItem(`${storageKey}_iv`)
    const mode = localStorage.getItem(`${storageKey}_mode`)
    const keySize = localStorage.getItem(`${storageKey}_keySize`)

    // 设置状态
    if (sourceValue) state.sourceValue = sourceValue
    if (key) state.key = key
    if (iv) state.iv = iv
    if (mode) state.mode = mode
    if (keySize) state.keySize = keySize
    change(false)
})
import { open } from "@tauri-apps/plugin-dialog"
async function openf() {
    const file = await open({
        multiple: false,
        directory: false,
        filters: [
            {
                name: "所有文件",
                extensions: ["*"],
            },
            {
                name: "文本文件",
                extensions: ["txt", "md"],
            },
            {
                name: "图片文件",
                extensions: ["png", "jpg", "jpeg", "gif"],
            },
        ],
        defaultPath: "~/Documents", // 默认打开路径
        title: "请选择文件1", // 对话框标题
    })
    console.log(file)
}
</script>

<template>
    <div class="k f">
        <div class="content f_col">
            <h1 class="f_row" style="justify-content: flex-start">
                AES加解密<copyw title="AES加解密" ckey="doaes"></copyw>
            </h1>

            <div class="f_row" style="justify-content: flex-start; gap: 20px; flex-wrap: wrap">
                <a-input-password
                    v-model:value="state.key"
                    placeholder="请输入密钥"
                    style="width: 300px"
                    @change="change"
                />
                <a-input-password
                    v-model:value="state.iv"
                    placeholder="请输入IV (CBC模式必填)"
                    style="width: 200px"
                    :disabled="state.mode === 'ECB'"
                    @change="change"
                />
                <a-select v-model:value="state.mode" style="width: 120px" @change="change">
                    <a-select-option value="ECB">ECB模式</a-select-option>
                    <a-select-option value="CBC">CBC模式</a-select-option>
                </a-select>
                <a-select v-model:value="state.keySize" style="width: 120px" @change="change">
                    <a-select-option value="128">128位</a-select-option>
                    <a-select-option value="192">192位</a-select-option>
                    <a-select-option value="256">256位</a-select-option>
                </a-select>
            </div>

            <div style="width: 100%">
                <h3>源文本</h3>
                <a-textarea
                    class="kuang"
                    v-model:value="state.sourceValue"
                    allow-clear
                    show-count
                    placeholder="请输入待处理的文本"
                    :auto-size="{ minRows: 6, maxRows: 15 }"
                    @change="change"
                    autofocus
                />
            </div>

            <div style="width: 100%">
                <div class="f_row" style="justify-content: flex-start">
                    <h3>加密结果</h3>
                    <a style="margin-bottom: 8px; margin-left: 12px; cursor: pointer" @click="copy(state.encryptData)"
                        >复制</a
                    >
                </div>
                <div v-text="state.encryptData" class="auto-wrap"></div>
            </div>

            <div style="width: 100%">
                <div class="f_row" style="justify-content: flex-start">
                    <h3>解密结果</h3>
                    <a style="margin-bottom: 8px; margin-left: 12px; cursor: pointer" @click="copy(state.decryptData)"
                        >复制</a
                    >
                </div>
                <div v-text="state.decryptData" class="auto-wrap"></div>
            </div>
        </div>
    </div>
</template>

<style scoped>
.content {
    max-width: 860px;
    width: 100%;
    margin: 0 auto;
    padding: 32px;
    gap: 24px;
    align-items: flex-start;
    min-height: calc(100vh - 66px);
    justify-content: flex-start;
    background-color: var(--Neutral-12);
    border-radius: 12px;
    border: 1px solid var(--Neutral-10);
}

:where(.k) {
    background-color: var(--page-bg-color);
    height: 100%;
    padding: 16px;
}

.auto-wrap {
    white-space: pre-wrap;
    word-wrap: break-word;
    min-height: 50px;
    padding: 12px 16px;
    background-color: var(--Neutral-11);
    border: 1px solid var(--Neutral-10);
    border-radius: 8px;
    color: var(--character-85);
}

h1 {
    color: var(--character-85);
    font-size: 24px;
    margin-bottom: 8px;
}

h3 {
    color: var(--character-75);
    font-size: 16px;
    margin-bottom: 8px;
}

a {
    color: var(--Geek-Blue-5);
    transition: color 0.3s ease;
}

a:hover {
    color: var(--Geek-Blue-4);
}

:deep(.ant-input-password),
:deep(.ant-select:not(.ant-select-customize-input) .ant-select-selector) {
    background-color: var(--Neutral-11) !important;
    border: 1px solid var(--Neutral-10) !important;
    color: var(--character-85) !important;
    border-radius: 8px;
    transition: all 0.3s ease;
}

:deep(.ant-input-password:hover),
:deep(.ant-select:hover .ant-select-selector) {
    border-color: var(--Neutral-9) !important;
}

:deep(.ant-input-password:focus),
:deep(.ant-select-focused .ant-select-selector) {
    border-color: var(--Neutral-8) !important;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.05) !important;
}

:deep(.ant-textarea) {
    background-color: var(--Neutral-11) !important;
    border: 1px solid var(--Neutral-10) !important;
    color: var(--character-85) !important;
    border-radius: 8px;
}
</style>
